f53890
@@ -17,17 +17,21 @@
 package org.apache.camel.processor;
 
 import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.AsyncProcessor;
 import org.apache.camel.AsyncProducerCallback;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
+import org.apache.camel.ErrorHandlerFactory;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
 import org.apache.camel.Expression;
 import org.apache.camel.FailedToCreateProducerException;
 import org.apache.camel.Message;
+import org.apache.camel.Processor;
 import org.apache.camel.Producer;
 import org.apache.camel.Traceable;
 import org.apache.camel.builder.ExpressionBuilder;
@@ -36,9 +40,12 @@
import org.apache.camel.impl.EmptyProducerCache;
 import org.apache.camel.impl.ProducerCache;
 import org.apache.camel.spi.EndpointUtilizationStatistics;
 import org.apache.camel.spi.IdAware;
+import org.apache.camel.spi.RouteContext;
+import org.apache.camel.spi.UnitOfWork;
 import org.apache.camel.support.ServiceSupport;
 import org.apache.camel.util.AsyncProcessorHelper;
 import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.KeyValueHolder;
 import org.apache.camel.util.MessageHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.ServiceHelper;
@@ -67,6 +74,20 @@
public class RoutingSlip extends ServiceSupport implements AsyncProcessor, Trace
     protected Expression expression;
     protected String uriDelimiter;
     protected final CamelContext camelContext;
+    private final ConcurrentMap<PreparedErrorHandler, AsyncProcessor> errorHandlers = new ConcurrentHashMap<PreparedErrorHandler, AsyncProcessor>();
+
+    /**
+     * Class that represents prepared fine grained error handlers when processing routingslip/dynamic-router exchanges
+     * <p/>
+     * This is similar to how multicast processor does.
+     */
+    static final class PreparedErrorHandler extends KeyValueHolder<RouteContext, Processor> {
+
+        public PreparedErrorHandler(RouteContext key, Processor value) {
+            super(key, value);
+        }
+
+    }
 
     /**
      * The iterator to be used for retrieving the next routing slip(s) to be used.
@@ -304,6 +325,49 @@
public class RoutingSlip extends ServiceSupport implements AsyncProcessor, Trace
         return copy;
     }
 
+    protected AsyncProcessor createErrorHandler(RouteContext routeContext, Exchange exchange, AsyncProcessor processor) {
+        AsyncProcessor answer = processor;
+
+        boolean tryBlock = exchange.getProperty(Exchange.TRY_ROUTE_BLOCK, false, boolean.class);
+
+        // do not wrap in error handler if we are inside a try block
+        if (!tryBlock && routeContext != null) {
+            // wrap the producer in error handler so we have fine grained error handling on
+            // the output side instead of the input side
+            // this is needed to support redelivery on that output alone and not doing redelivery
+            // for the entire routingslip/dynamic-router block again which will start from scratch again
+
+            // create key for cache
+            final PreparedErrorHandler key = new PreparedErrorHandler(routeContext, processor);
+
+            // lookup cached first to reuse and preserve memory
+            answer = errorHandlers.get(key);
+            if (answer != null) {
+                log.trace("Using existing error handler for: {}", processor);
+                return answer;
+            }
+
+            log.trace("Creating error handler for: {}", processor);
+            ErrorHandlerFactory builder = routeContext.getRoute().getErrorHandlerBuilder();
+            // create error handler (create error handler directly to keep it light weight,
+            // instead of using ProcessorDefinition.wrapInErrorHandler)
+            try {
+                answer = (AsyncProcessor) builder.createErrorHandler(routeContext, processor);
+
+                // must start the error handler
+                ServiceHelper.startServices(answer);
+
+                // add to cache
+                errorHandlers.putIfAbsent(key, answer);
+
+            } catch (Exception e) {
+                throw ObjectHelper.wrapRuntimeCamelException(e);
+            }
+        }
+
+        return answer;
+    }
+
     protected boolean processExchange(final Endpoint endpoint, final Exchange exchange, final Exchange original,
                                       final AsyncCallback callback, final RoutingSlipIterator iter) {
 
@@ -313,6 +377,11 @@
public class RoutingSlip extends ServiceSupport implements AsyncProcessor, Trace
         boolean sync = producerCache.doInAsyncProducer(endpoint, exchange, null, callback, new AsyncProducerCallback() {
             public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, final Exchange exchange,
                                              ExchangePattern exchangePattern, final AsyncCallback callback) {
+
+                // rework error handling to support fine grained error handling
+                RouteContext routeContext = exchange.getUnitOfWork() != null ? exchange.getUnitOfWork().getRouteContext() : null;
+                asyncProducer = createErrorHandler(routeContext, exchange, asyncProducer);
+
                 // set property which endpoint we send to
                 exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri());
                 exchange.setProperty(Exchange.SLIP_ENDPOINT, endpoint.getEndpointUri());
@@ -403,11 +472,14 @@
public class RoutingSlip extends ServiceSupport implements AsyncProcessor, Trace
     }
 
     protected void doStop() throws Exception {
-        ServiceHelper.stopService(producerCache);
+        ServiceHelper.stopServices(producerCache, errorHandlers);
     }
 
     protected void doShutdown() throws Exception {
-        ServiceHelper.stopAndShutdownService(producerCache);
+        ServiceHelper.stopAndShutdownServices(producerCache, errorHandlers);
+
+        // only clear error handlers when shutting down
+        errorHandlers.clear();
     }
 
     public EndpointUtilizationStatistics getEndpointUtilizationStatistics() {
